package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.dto.RetreatApplyDto;
import com.zzyl.dto.RetreatClearingBillDto;
import com.zzyl.entity.*;
import com.zzyl.enums.*;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.*;
import com.zzyl.service.BillService;
import com.zzyl.service.RetreatService;
import com.zzyl.service.TradingService;
import com.zzyl.utils.CodeUtil;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.*;
import com.zzyl.vo.retreat.DueBack;
import com.zzyl.vo.retreat.RetreatBillVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 退住管理
 *
 * @author itcast
 * @create 2023/12/20 16:24
 **/
@Service
public class RetreatServiceImpl implements RetreatService {

    @Autowired
    private ContractMapper contractMapper;
    @Autowired
    private RetreatMapper retreatMapper;
    @Autowired
    private RetreatBillMapper retreatBillMapper;
    @Autowired
    private NursingTaskMapper nursingTaskMapper;
    @Autowired
    private NursingElderMapper nursingElderMapper;
    @Autowired
    private BillService billService;
    @Autowired
    private ElderMapper elderMapper;
    @Autowired
    private BedMapper bedMapper;
    @Autowired
    private CheckInMapper checkInMapper;
    @Autowired
    private TradingService tradingService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 退住编号前缀
     */
    private static final String RETREAT_CODE_PREFIX = "TZ";

    /**
     * 申请退住
     *
     * @param retreatApplyDto 申请退住请求模型
     */
    @Override
    public void apply(RetreatApplyDto retreatApplyDto) {
        //校验退住时间是否在费用期间
        LocalDateTime checkOutTime = retreatApplyDto.getRetreatElderDto().getCheckOutTime();
        LocalDateTime costStartTime = retreatApplyDto.getRetreatElderDto().getCostStartTime();
        LocalDateTime costEndTime = retreatApplyDto.getRetreatElderDto().getCostEndTime();
        if (checkOutTime.isBefore(costStartTime) || checkOutTime.isAfter(costEndTime)) {
            throw new BaseException(BasicEnum.RETREAT_SHOULD_IN_COST_TERM);
        }

        //从当前线程中获取用户数据
        String subject = UserThreadLocal.getSubject();
        UserVo user = JSON.parseObject(subject, UserVo.class);

        //老人id
        Long elderId = retreatApplyDto.getRetreatElderDto().getId();

        //1.修改合同
        Contract contract = updateContract(retreatApplyDto, user.getRealName(), elderId);

        //2.新增退住信息
        Retreat retreat = insertRetreat(retreatApplyDto, user, contract);

        //3.新增退款账单信息
        insertRetreatBill(retreatApplyDto, retreat.getId());

        //4.清空未执行的护理任务
        clearNursingTask(elderId);

        //5.护理员解绑
        nursingElderMapper.deleteByElderId(elderId);

        //6.处理账单和老人账户
        RetreatBillVo retreatBillVo = JSONUtil.toBean(retreatApplyDto.getBillJson(), RetreatBillVo.class);
        handleBillAndBalance(elderId, checkOutTime, retreatBillVo);

        //7.更新床位状态
        Elder elder = elderMapper.selectByPrimaryKey(elderId);
        bedMapper.updateBedStatusById(elder.getBedId(), BedStatusEnum.UNOCCUPIED.getOrdinal());

        //8.更新入住记录状态
        checkInMapper.updateStatusByElderId(elderId, CheckInStatusEnum.RETREAT.getOrdinal());

        //9.更新老人数据
        updateElder(elderId);
    }

    /**
     * 更新老人信息
     *
     * @param elderId 老人id
     */
    private void updateElder(Long elderId) {
        Elder elder = new Elder();
        elder.setId(elderId);
        elder.setStatus(ElderStatusEnum.RETREAT.getOrdinal());
        elder.setBedNumber("");
        elder.setBedId(-1L);
        elderMapper.updateByPrimaryKeySelective(elder);
    }

    /**
     * 处理账单和老人账户
     *
     * @param elderId       老人id
     * @param checkOutTime  退住时间
     * @param retreatBillVo 退住账单数据
     */
    private void handleBillAndBalance(Long elderId, LocalDateTime checkOutTime, RetreatBillVo retreatBillVo) {
        //处理服务费用退款
        List<DueBack> dueBackList = retreatBillVo.getDueBackList();
        dueBackList.stream()
                .filter(x -> x.getType() == 1)
                .forEach(v -> {
                    TradingVo tradingVo = new TradingVo();
                    tradingVo.setTradingOrderNo(v.getTradingOrderNo());
                    tradingVo.setCreateType(2);
                    tradingService.refundTrading(tradingVo);
                });


        RetreatClearingBillDto retreatClearingBillDto = new RetreatClearingBillDto();

        //筛选出应退月度账单
        if (CollUtil.isNotEmpty(dueBackList)) {
            retreatClearingBillDto.setDueBackList(dueBackList.stream().filter(v -> v.getType() == 0).collect(Collectors.toList()));
        }

        //实际退住时间
        retreatClearingBillDto.setLocalDateTime(checkOutTime);

        //老人id
        retreatClearingBillDto.setElderId(elderId);

        BalanceVo balanceVo = retreatBillVo.getBalanceVo();
        //余额扣减金额
        BigDecimal depositDeductions = balanceVo.getDepositAmount().subtract(balanceVo.getArrearsAmount());
        //押金备注
        retreatClearingBillDto.setDepositRemark(balanceVo.getRemark());
        //押金扣减金额
        retreatClearingBillDto.setDepositDeductions(depositDeductions.compareTo(BigDecimal.ZERO) == 1 ? depositDeductions : new BigDecimal(0));

        //关闭账户，清空数据
        billService.retreatClearingBill(retreatClearingBillDto);
    }

    /**
     * 清理护理任务
     *
     * @param elderId 老人id
     */
    private void clearNursingTask(Long elderId) {
        NursingTask nursingTask = new NursingTask();
        nursingTask.setElderId(elderId);
        nursingTask.setStatus(NursingTaskStatusEnum.WAITING.getOrdinal());
        nursingTaskMapper.deleteByElderIdAndStatus(nursingTask);
    }

    /**
     * 新增退住账单
     *
     * @param retreatApplyDto 退住申请
     * @param retreatId       退住id
     */
    private void insertRetreatBill(RetreatApplyDto retreatApplyDto, Long retreatId) {
        RetreatBill retreatBill = BeanUtil.toBean(retreatApplyDto.getRefundVoucherDto(), RetreatBill.class);
        retreatBill.setRetreatId(retreatId);
        retreatBill.setBillJson(retreatApplyDto.getBillJson());

        //如果没有退款金额，金额默认设置为0
        BigDecimal refundAmount = Optional.ofNullable(retreatApplyDto.getRefundVoucherDto().getRefundAmount()).orElse(BigDecimal.ZERO);
        retreatBill.setRefundAmount(refundAmount);
        retreatBillMapper.insert(retreatBill);
    }

    /**
     * 新增退住信息
     *
     * @param retreatApplyDto 退住申请
     * @param user            当前用户信息
     * @param contract        合同信息
     * @return 退住信息
     */
    private Retreat insertRetreat(RetreatApplyDto retreatApplyDto, UserVo user, Contract contract) {
        Retreat retreat = BeanUtil.toBean(retreatApplyDto.getRetreatElderDto(), Retreat.class);
        retreat.setElderId(retreatApplyDto.getRetreatElderDto().getId());

        //退住标题
        String title = retreat.getName() + "的退住申请";
        retreat.setTitle(title);

        //退住编号
        String retreatCode = CodeUtil.generateCode(RETREAT_CODE_PREFIX, redisTemplate, 5);
        retreat.setRetreatCode(retreatCode);

        //合同信息
        retreat.setContractName(contract.getName());
        retreat.setContractNo(contract.getContractNo());
        retreat.setContractUrl(retreatApplyDto.getReleasePdfUrl());

        //申请人信息
        retreat.setApplicatId(user.getId());
        retreat.setApplicat(user.getRealName());
        retreat.setDeptNo(user.getDeptNo());
        retreatMapper.insert(retreat);
        return retreat;
    }

    /**
     * 更新合同
     *
     * @param retreatApplyDto 退住申请信息
     * @param userName        当前用户名称
     * @param elderId         老人id
     * @return 合同
     */
    private Contract updateContract(RetreatApplyDto retreatApplyDto, String userName, Long elderId) {
        Contract contract = contractMapper.selectLastByElderId(elderId);
        contract.setReleaseDate(retreatApplyDto.getReleaseDate());
        contract.setReleasePdfUrl(retreatApplyDto.getReleasePdfUrl());
        contract.setReleaseSubmitter(userName);
        contract.setStatus(ContractStatusEnum.UN_EFFECTIVE.getOrdinal());
        contractMapper.updateByPrimaryKeySelective(contract);
        return contract;
    }

    /**
     * 分页查询退住信息
     *
     * @param elderName        老人姓名，模糊查询
     * @param elderIdCardNo    身份证号，精确查询
     * @param retreatStartTime 退住开始时间，格式：yyyy-MM-dd HH:mm:ss
     * @param retreatEndTime   退住结束时间，格式：yyyy-MM-dd HH:mm:ss
     * @param pageNum          页码
     * @param pageSize         页面大小
     * @return 分页结果
     */
    @Override
    public PageResponse<RetreatPageQueryVo> pageQuery(String elderName, String elderIdCardNo, LocalDateTime retreatStartTime, LocalDateTime retreatEndTime, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);

        //老人姓名，模糊查询，这里进行预处理
        elderName = CharSequenceUtil.isBlank(elderName) ? null : "%" + elderName + "%";
        Page<RetreatPageQueryVo> pageResult = retreatMapper.pageQuery(elderName, elderIdCardNo, retreatStartTime, retreatEndTime);
        return PageResponse.of(pageResult, RetreatPageQueryVo.class);
    }

    /**
     * 根据退住id查询详情
     *
     * @param id 退住id
     * @return 退住详情
     */
    @Override
    public RetreatDetailVo detail(Long id) {
        //退住信息
        Retreat retreat = retreatMapper.selectById(id);

        //合同信息
        Contract contract = contractMapper.selectByContractNo(retreat.getContractNo());

        //退住账单
        RetreatBill retreatBill = retreatBillMapper.selectByRetreatId(id);

        //封装退住详情
        RetreatDetailVo retreatDetailVo = new RetreatDetailVo();
        retreatDetailVo.setRetreatElderVo(BeanUtil.toBean(retreat, RetreatElderVo.class));
        retreatDetailVo.setReleaseDate(contract.getReleaseDate());
        retreatDetailVo.setReleasePdfUrl(contract.getReleasePdfUrl());
        retreatDetailVo.setBillJson(retreatBill.getBillJson());
        retreatDetailVo.setRefundVoucherVo(BeanUtil.toBean(retreatBill, RefundVoucherVo.class));
        return retreatDetailVo;
    }
}
